import os
from stop_condition import StopCondition
import select
import pickle
import function
import wx
import wx.lib.plot as plot
import numpy as numarray
import random
import sys
import wizard
import threading
import wx.lib.newevent
import time
from copy import deepcopy

from mutation import Mutation
from crossover import Crossover
from function import Problem
from population import Population


class Project:
	def __init__(self):
		self.project_name = None
		self.parameters = None
		self.function = None
		self.stop_condition = None
		self.population = None
		self.mutation = None
		self.crossover = None
		self.parent_selection = None
		self.survivor_selection = None
	
	def __str__(self):
		s = "\nPROJECT!"
		s+="\n\tNAME\t"+str(self.project_name)
		s+="\n\tTYPES\t"+str(self.stop_condition)
		s+="\n\tPROB\t"+str(self.function)
		s+="\n\tSTOP\t"+str(self.stop_condition)
		s+="\n\tPOPSTATS\t"+str(self.population)
		s+="\n\tMUTATION\t"+str(self.mutation)
		s+="\n\tRECOMB\t"+str(self.crossover)
		s+="\n\tPARENTS\t"+str(self.parent_selection)
		s+="\n\tSURVIVALISM\t"+str(self.survivor_selection)+"\n\n"
		return s

	
	def load_project(self, filename):
		####CHANGES
		f_bin=file(filename, "r")
		self.project_name=pickle.load(f_bin)
		self.parameters=pickle.load(f_bin)
		self.stop_condition=pickle.load(f_bin)
		self.function=pickle.load(f_bin)
		self.population=pickle.load(f_bin)
		self.mutation=pickle.load(f_bin)
		self.crossover=pickle.load(f_bin)
		self.parent_selection=pickle.load(f_bin)
		self.survivor_selection=pickle.load(f_bin)
		self.file_name = str(self.project_name) + "\\" + str(self.project_name)
		aux = os.getcwd()
		a = aux[:-len(self.project_name)]
		os.chdir(a)
		f_bin.close()


	def set_project(self, project_name, parameters, function, stop_condition,\
				 population, mutation, crossover, parent_selection, survivor_selection):
		##Longshot:
		##parameters = [project_type("Quickshot", "Longshot"), algorithm_type("AG", "EE"), number_runs]
		##function = Class Function
		##stop_condition = [callable_string, [number_generations, target_quality]]
		##population = [range(size_population)]
		##mutation = [callable_string, range(Prob), range(alpha), range(sigma)]
		##crossover = [callable_string, range(prob), range(numPoints)]
		##parent_selection = [callable_string, range(arity),range(num_contestants), range(losers_hype), range(high_prob)]
		##SurvivorSelection = [string (callable), range(elitism), range(pop_div), range(best_pass)]
		##On Quickshot range lists are replaced with unitary lists with a single value
		##If parameter is not needed, the list element must be none
		self.project_name = project_name
		self.parameters = parameters
		self.function = function
		self.stop_condition = stop_condition
		self.population = population
		self.mutation = mutation
		self.crossover = crossover
		self.parent_selection = parent_selection
		self.survivor_selection = survivor_selection
		self.file_name = self.project_name + "\\" + self.project_name
		self.create_config_file()

	
	def start_runs(self, MainWindow, GenEvent, GAEvent, EndLSEvent):
		res = []
		iter = 0
		ga_num = 0
		for mut_prob in self.mutation[1]:
			for mut_alpha in self.mutation[2]:
				for mut_sigma in self.mutation[3]:
					for cross_prob in self.crossover[1]:
						for cross_npoints in self.crossover[2]:
							for psel_arity in self.parent_selection[1]:
								for psel_ncontestants in self.parent_selection[2]:
									for psel_losers in self.parent_selection[3]:
										for ssel_elitism in self.survivor_selection[1]:
											for pop_size in self.population:
												for best in self.survivor_selection[2]:
													for bestP in self.survivor_selection[3]:
														for highP in self.parent_selection[4]:
															sums = [0,0]
															for runs in range(0,self.parameters[2]):
																file_name = self.project_name + "_" + str(iter) + "_" + str(runs)
																psel_list = [psel_arity,psel_ncontestants,psel_losers,highP]
																ssel_list = [ssel_elitism, [best, bestP]]
																cross_list = [cross_prob, cross_npoints]
																mut_list = [ mut_prob , mut_alpha , mut_sigma]	
																ag = AG(self.function,	self.parameters[1],	pop_size,
																		self.function.get_genesize_list(), self.stop_condition[0],
																		self.stop_condition[1],	self.parent_selection[0],
																		psel_list, psel_arity,self.survivor_selection[0],
																		ssel_list, self.mutation[0],
																		mut_list,  self.crossover[0],
																		cross_list,file_name)
																res.append(ag.evolution(GenEvent, MainWindow))
																sums[0]+=res[iter][0].best().get_fitness()
																sums[1]+=res[iter][0].average()
																self.update_project_results(res[iter][0], res[iter][1], iter)
																iter += 1
															self.update_longshot_values(ga_num, MainWindow, GAEvent, psel_list,\
																						    ssel_list, cross_list, mut_list, \
																						    sums, pop_size, self.parameters[2])
															ga_num+=1
		if(self.parameters[0]=="Longshot"):
			self.final_longshot_values( EndLSEvent, MainWindow, res)
	
	
	def final_longshot_values(self, event, window, results):
		##Sends Information for Longshot Chart
		event =event(results=results)
		wx.PostEvent(window, event)	

	def update_longshot_values(self, i, window, event, psel_list, ssel_list,\
							   cross_list, mut_list, sums, pop_size, nruns):
		##Updates GUI with information about each GA runs
		
		event = event(iter = i, par_list = [sums[0], sums[1], nruns, pop_size, \
									    	psel_list[0], psel_list[1], psel_list[2],\
									   		cross_list[0], cross_list[1],\
											mut_list[1], mut_list[0], ssel_list[0]])
		wx.PostEvent(window, event)

	def update_project_results(self, pop, generation, iter =0):
		##Binary Results file
		f_bin = file(self.project_name+"\\"+self.project_name+"_results"+".dat", "a")
		pickle.dump(iter, f_bin)
		pickle.dump(generation, f_bin)
		pickle.dump(pop, f_bin)
		
		##Text Results file
		f_txt = file(self.project_name+"\\"+self.project_name+"_results"+".txt", "a")
		f_txt.write ("Iteration number :" + str(iter) + "\n")
		f_txt.write ("Generation number :" + str(generation)+ "\n")
		f_txt.write ("Best individual: " + str(pop.best()) + " with fitness: "\
					  + str(pop.best().fitness)+ "\n")
		f_txt.write ("Fitness Average: " + str(pop.average())+ "\n")
		f_txt.write ("Standard Deviation:" + str(pop.deviation())+ "\n")
		f_txt.write ("---------------------------------------------" + "\n")	
		
	
	
	def create_config_file(self,first_write=True):
		#if first_write:
		#	os.mkdir(self.project_name)
		try:
			os.mkdir(self.project_name)	
		except: 
			pass 	
		##Binary Configuration file
		f_bin = file(self.file_name+"_config"+".proj", "w")
		pickle.dump(self.project_name, f_bin)
		pickle.dump(self.parameters, f_bin)
		pickle.dump(self.stop_condition, f_bin)
		pickle.dump(self.function, f_bin)
		pickle.dump(self.population, f_bin)
		pickle.dump(self.mutation, f_bin)
		pickle.dump(self.crossover, f_bin)
		pickle.dump(self.parent_selection, f_bin)
		pickle.dump(self.survivor_selection, f_bin)
		
		##Text Configuration file
		f_txt = file(self.file_name+"_config"+".txt", "w")
		##Text Version
		f_txt.write ("Project " + self.project_name + "\n")
		f_txt.write ("Function: " + self.function.my_problem + "\n")
		f_txt.write ("Algorithm: " + self.parameters[1] + "\n")
		f_txt.write ("Population Size: " + str(self.population[0]) + "\n")
		f_txt.write ("Chromossome size (in genes - for each variable): " \
					 + str(self.function.get_genesize_list()) + "\n")
		f_txt.write ("\n")
		
		f_txt.write ("Stop Criteria: " + self.stop_condition[0] + "\n")
		f_txt.write ("Stop Criteria Parameters: ")
		f_txt.write ("\n\tTarget Quality Required: "+str(self.stop_condition[1][0]))
		f_txt.write ("\n\tNumber of Generations: "+str(self.stop_condition[1][1]))
		f_txt.write ("\n")
		
		f_txt.write ("Parent Selection: " + self.parent_selection[0] + "\n")
		f_txt.write ("Parent Selection Parameters: ")
		f_txt.write ("\n\tNumber of Parents: " + str(self.parent_selection[1]))
		f_txt.write ("\n\tNumber of Contestants (Tournament Only): "+str(self.parent_selection[2]))
		f_txt.write ("\n\tLosers Hype (Tournament Only): "+str(self.parent_selection[3]))
		f_txt.write ("\n")
		f_txt.write ("\n")
		             
		f_txt.write ("Survivor Selection: " + self.survivor_selection[0]+ "\n")
		f_txt.write ("Survivor Selection Parameters: ")
		f_txt.write ("\n\tElitism: "+str(self.survivor_selection[1]))
		f_txt.write ("\n\tPercentage: "+str(self.survivor_selection[2]))
		f_txt.write ("\n")
		f_txt.write ("\n")
		
		f_txt.write ("Mutation:" + self.mutation[0] + "\n")
		f_txt.write ("Mutation Parameters: ")
		f_txt.write ("\n\tProbability: "+str(self.mutation[1]))
		f_txt.write ("\n\tAlpha: "+str(self.mutation[2]))
		f_txt.write ("\n\tSigma: "+str(self.mutation[3]))
		
		f_txt.write ("Crossover:" + self.crossover[0] + "\n")
		f_txt.write ("Crossover Parameters: ")
		f_txt.write ("\n\tProbability: "+str(self.crossover[1]))
		f_txt.write ("\n\tPontos Corte: "+str(self.crossover[2]))
		f_txt.write ("\n\tPeso: "+str(self.crossover[3]))
		
		f_txt.close ()
		
		
	def gimme_string_config(self):
		output="Project Name: "+self.project_name +"\n\n"
		
		##FUNCTION STUFF##
		output+="\tProblem:\n"
		output+="\t\tFunction: "+self.function.my_problem
		output+="\n\n"
		
		##ALGORITHM STUFF##
		output+="\tGenetic Algorithm:\n"
		output+="\t\tGeneral: \n"
		output+="\t\t\tAlgorithm: " + self.parameters[1] + "\n"
		output+="\n\n"
		
		##POPULATION STUFF##
		output+="\tPopulation:\n"
		output+="\t\tSize:" +str(self.population[0])+ "\n"
		output+="\t\tIndividual Size: " + str(self.function.get_genesize_list()) + "\n"
		output+="\n\n"
		
		##SELECTION STUFF STUFF##
		output+="\tParent Selection:\n"
		output+="\t\tMethod:" +self.parent_selection[0]+ "\n"
		output+="\n\n"
		
		return output
		
	

class AG:

	def __init__(self,problem, evol_type,size_population,size_individual,\
				 stop_string, stop_par_list, select_parent_string, select_parent_list,\
				 arity,	select_survivors_string, select_survivors_list,	mutation_string,\
				 mut_par_list, crossover_string, cross_par_list, file):
				
		def parameter_treat(par_list):
			parameters=""
			if(len(par_list)>0):
				parameters = str(par_list[0])
				for i in range(1, len(par_list)):
					parameters=parameters+","+str(par_list[i])
			return parameters
		
		### Mutation Treatment ###
		obj_mutation = Mutation()
		par = parameter_treat(mut_par_list)
		self.mut_f = eval("obj_mutation."+mutation_string+"("+par+")")
		
		### Crossover Treatment ###
		obj_crossover = Crossover(arity)
		par = parameter_treat(cross_par_list)
		self.cross_f = eval("obj_crossover."+crossover_string+"("+par+")")
		
		### Stop Treatment ###
		obj_stop = StopCondition(stop_par_list) # (max_gen,best_wanted)
		self.stop_f = eval("obj_stop."+stop_string+"()")
		
		### Parent Selection Treatment ###
		obj_p_select = select.SelectParents() #recebe lista com os parametros needed
		par = parameter_treat(select_parent_list)
		self.select_p_f = eval("obj_p_select."+select_parent_string+"("+par+")")
		
		### Survivors Selection Treatment ###
		par = parameter_treat(select_survivors_list)
		obj_s_select = select.SelectSurvivors() #recebe lista com os parametros needed
		self.select_s_f = eval("obj_s_select."+select_survivors_string+"("+par+")")
		
		
		self.problem = problem					# Problem Object 

		self.size_population = size_population	# Size of the Population
		self.size_individual = size_individual	 # List of genesize elements 
		self.write_file=file					# File for log
		self.evol_type=evol_type				# EE ou AG

	def evolution(self, event, window):
		
		## Population Initialization ##
		pop = Population("Init",self.size_population,self.size_individual,self.evol_type)
		pop.evaluation(self.problem.eval,0)				
		## Generation counter ##
		num_ger = 0
		self.draw(pop, num_ger, window, event)
		
		### MAIN LOOP ###
		
		while(self.stop_f(num_ger, (pop.best()).fitness) != True): # recebe SMP esses dois!
				
			parents = self.select_p_f(pop)					# Select pais
			npop = self.cross_f(parents)					# Crossover
			npop = self.mut_f(npop, num_ger)				# Mutation			
			npop.evaluation(self.problem.eval,num_ger)		# Eval new guys value
			pop = self.select_s_f(npop, pop)				# Select the best of 'em
			pop.update_stats()
			num_ger += 1
			self.draw(pop, num_ger, window, event)
			self.write_to_file(pop, num_ger)
			
		if num_ger == 0:
			num_ger+=1
			self.draw(pop, num_ger, window, event)
		
		self.draw(pop, num_ger, window, event)
		
		return [deepcopy(pop), num_ger]
	
	
	
	def draw(self, pop, gen, window, event):
		##Method for drawing graphics
		if gen == 0:
			event = event(generation = 0, best = 0, avg = 0, stdev = 0, pop=pop)
		else:
			event = event(generation = gen, best = (pop.best().get_fitness()),\
					  avg = pop.average(), stdev = pop.deviation() , pop = pop)
		wx.PostEvent(window, event)
		
			
	def write_to_file(self, pop, num_ger):
		##Method for log
		
		##Binary Data Save:
		##To Load, use: pop = pickle.load(file)
		pois = self.write_file
		a = pois.split('_')
		directory =  a[0]
		f_bin = file(directory+"\\"+self.write_file[:-2]+".dat", "w")
		pickle.dump(pop, f_bin)
		##Text Data Save:
		f_txt = file(directory+"\\"+self.write_file[:-2]+".txt", "a")
		f_txt.write ("Generation number :" + str(num_ger)+ "\n")
		f_txt.write ("Best Individual: " + str(pop.best()) + " with fitness: "\
					  + str(pop.best().get_fitness())+ "\n")
		f_txt.write ("Fitness Average: " + str(pop.average())+ "\n")
		f_txt.write ("Standard Deviation:" + str(pop.deviation())+ "\n")
		f_txt.write ("---------------------------------------------" + "\n")

	def create_files(self, stop_string, stop_par_list,select_parent_string,\
					 select_parent_list, arity, \
					 select_survivors_string, select_survivors_list, \
					 mutation_string, mut_par_list, \
					 crossover_string, cross_par_list, \
					 obj_stop, obj_mutation, obj_s_select,
					 obj_p_select, obj_crossover):
		
		##Binary version:
		directory = self.write_file[:-2]
		f_bin = file(directory+"\\"+self.write_file+".dat", "w")
		pickle.dump(self.problem,f_bin)
		pickle.dump(self.evol_type,f_bin)
		pickle.dump(self.size_population,f_bin)
		pickle.dump(self.size_individual,f_bin)
		pickle.dump(obj_stop,f_bin)
		#pickle.dump(self.stop_f,f_bin)
		pickle.dump(obj_p_select,f_bin)
		#pickle.dump(self.select_p_f,f_bin)
		pickle.dump(obj_s_select,f_bin)
		#pickle.dump(self.select_s_f,f_bin)
		pickle.dump(obj_mutation,f_bin)
		#pickle.dump(self.mut_f,f_bin)
		pickle.dump(obj_crossover,f_bin)
		#pickle.dump(self.cross_f,f_bin)
		f_bin.close()
		
		##Text Version:
		directory = self.write_file[:-2]
		f_txt = file(directory+"\\"+self.write_file+".txt", "w")
		f_txt.write ("Function: " + self.problem.my_problem + "\n")
		f_txt.write ("Algorithm: " + self.evol_type + "\n")
		f_txt.write ("Population Size: " + str(self.size_population) + "\n")
		f_txt.write ("Chromossome size (in genes): " + str(self.size_individual) + "\n")
		f_txt.write ("Stop Criterion: " + stop_string + "\n")
		f_txt.write ("Stop Criterion Parameters: " + str(stop_par_list) + "\n")
		f_txt.write ("Parent Selection: " + select_parent_string + "\n")
		f_txt.write ("Parent Selection Parameters: " + str(select_parent_list) + "\n")
		f_txt.write ("Survivor Selection: " + select_survivors_string + "\n")
		f_txt.write ("Survivor Selection Parameters: " + str(select_survivors_list) + "\n")
		f_txt.write ("Mutation:" + mutation_string + "\n")
		f_txt.write ("Mutation Parameters: " + str(mut_par_list) + "\n")
		f_txt.write ("Crossover:" + crossover_string + "\n")
		f_txt.write ("Crossover Parameters: " + str(cross_par_list) + "\n")
		f_txt.write ("---------------------------------------------" + "\n")
		f_txt.write ("---------------------------------------------" + "\n")
		f_txt.close ()
	
	def update(self, num_ger, pop, problem):
		##Method for updating variables...
		pass
			
